(function($) {
	// Create sao namespace
	$.sao		= {};
	
	// Create updater collection for registering jQuery.sao.Updater objects
	$.sao.updaters	= [];
	
	// Default settings for registering a jQuery.sao.Updater
	$.sao.settings	= {
	   /*
		lastModified	: 1313748004786;
		feed		: 'odds' | 'football-odds' | 'scores'	// feed type
		feedParam	: // paramater for feed
					'odds' -> 'YYYY-MM-DD' // Date to pull odds from
					'scores' -> 'NFL' | 'CFL' | 'FBC' | 'NBA' | 'BKC' | 'WNB' // Date to pull odds from
					'football-odds' -> none
		feedInterval	: 90	// in seconds
	   */
	   currentLeague: null,
	   startColor: '',
	   selectors: {
		   open		:	'.column-open',
		   current	:	'.column-current',
		   halftime	:	'.column-halftime',
		   score	:	'.column-score .score',
		   progress	:	'.column-score .progress',
		   period	:	'.column-score .period'
	   }
	};

	/**
	 * Takes a number and pads it with '0's until it reaches the provided length.
	 *
	 * @param {number} number The number that needs to be padded
	 * @param {number} length The number of characters the string should be padded to
	 * @return {string} The padded number as a string
	 */
	$.sao.pad = function pad(number, length) {
		var str = String(number);
		while (str.length < length) {
			str = '0' + str;
		}
		return str;
	};

	/**
	 * Creates a new Circle from a diameter.
	 *
	 * @this {jQuery}
	 * @param {string} data The data to update the jQuery collection with
	 * @param {Date} date The timestamp for when the data was last updated
	 * @return {jQuery} The jQuery collection
	 */
	$.sao.compare	= function (data, date)
	{
		return this.each(function() {
		
			var $this	= $(this);

			if(data !== undefined)
			{
				if($this.text() !== String(data) && date.valueOf() > $this.data('last-modified') )
				{
					$.sao.update.apply(this, [data]);
					$this.data('last-modified', date.valueOf());
				}
			}
		});
	};
	
	/**
	 * Creates a new Circle from a diameter.
	 *
	 * @this {DOMElement}
	 * @param {string} data The data to update the DOMElement with
	 */
	$.sao.update	= function (data)
	{
		var $this	= $(this);
		if($.sao.settings.startColor === ''){
			$.sao.settings.startColor = $this.css('color');
		}
		
		$this.text(data);
		$this.css('font-weight', 'bold');
		$this.animate({ color: "red" }, 1000, function() {
			$this.animate({ color: $.sao.settings.startColor }, 1000, function () {
				$this.css('font-weight', 'normal');
			});
		});
	};
	
	/**
	 * Creates a new feed object.  The feed object will create the feed uri
	 * provided the appropriate paramaters
	 *
	 * @constructor
	 * @this {jQuery.sao.Feed}
	 * @param {string} type 
	 * @param {string} param 
	 */
	$.sao.Feed	= function (type, param) {
		var uri	= '', now;
		
		/** @private */ this.type	= type;
		/** @private */ this.uri	= null;
		
		switch(type)
		{
			case 'odds':
				uri	= '/feeds/day/';
				if(typeof(param) === 'string')
				{
					uri += param;
				}
				else
				{
					now	= new Date();
					uri	+= String(now.getFullYear()) + $.sao.pad(now.getMonth() + 1, 2) + $.sao.pad(now.getDate(), 2); // Today's date
				}
				this.uri = uri;
			break;
			
			case 'scores':
				if(typeof(param) === 'string')
				{
					this.uri = '/feeds/scores/' + param;
				}
				else
				{
					return null;
				}
			break;
			
			case 'football-odds':
				this.uri	= '/feeds/football';
			break;
		}
	};
	
	$.sao.Feed.prototype.type	= null;
	$.sao.Feed.prototype.uri	= null;
	
	/**
	 * Creates a new Updater object.  When run, the updater will collect game
	 * information from the provided feed at a given interval and update any
	 * associated DOMElements based on the jQuery selectors provided.
	 *
	 * To ensure that the correct items are updated properly, the following
	 * data elements must be added to a home and away DOMElement containers.
	 * - The game-id data is the SAO Primary ID for the game
	 * - The away-team data is a boolean to show if the container is the away
	 * team for the game
	 * - The home-team data is a boolean to show if the container is the home
	 * team for the game
	 * 
	 * &lt;li class=".away" data-game-id="1234567" data-away-team="true"&gt;
	 *   &lt;span class="column-open"&gt; -5 &lt;/span&gt;
	 *   &lt;span class="column-current"&gt; -6 &lt;/span&gt;
	 *   &lt;span class="column-halftime"&gt; -5 &lt;/span&gt;
	 *   &lt;span class="column-score"&gt; 2 &lt;/span&gt;
	 * &lt;/li&gt;
	 * &lt;li class=".away" data-game-id="1234567" data-home-team="true"&gt;
	 *   &lt;span class="column-open"&gt; -5 &lt;/span&gt;
	 *   &lt;span class="column-current"&gt; -6 &lt;/span&gt;
	 *   &lt;span class="column-halftime"&gt; -5 &lt;/span&gt;
	 *   &lt;span class="column-score"&gt; 5 &lt;/span&gt;
	 *   &lt;span class="period"&gt; 5 &lt;/span&gt;
	 *   &lt;span class="time-left"&gt; 5 &lt;/span&gt;
	 * &lt;/li&gt;
	 *
	 * @constructor
	 * @this {jQuery.sao.Updater}
	 * @param {jQuery.sao.Feed} feed The Feed to collect data from to update games 
	 * @param {object} selectors The jQuery selectors for each of the data
	 * fields that can be updated 
	 * @param {number} interval The interval in seconds that the updater should
	 * collect data from the feed 
	 * @param {number|string} lastModified UTC timestamp or Date String for
	 * when the pages content was last updated.  When the updater collects
	 * data, it will check to see if the data was updated after this timestamp
	 * before making updates. It defaults to 0 so the content is always updated. 
	 */
	$.sao.Updater	= function (feed, selectors, interval, lastModified) {
		/** @public */ this.feed	= feed;
		/** @public */ this.selectors	= selectors;
		
		if(!isNaN(interval))
		{
			this.interval = interval;
		}
		if($.sao.parser.timestamp(lastModified))
		{
			this.lastModified	= $.sao.parser.timestamp(lastModified);
		}
	};
	
	$.sao.Updater.prototype.intervalID		= null;
	$.sao.Updater.prototype.interval		= 90;
	$.sao.Updater.prototype.selectors		= null;
	$.sao.Updater.prototype.feed			= null;
	$.sao.Updater.prototype.lastModified	= new Date(0);
	
	/**
	 * Begins the feed processing by the updator at the given interval
	 *
	 * @this {jQuery.sao.Updater}
	 */
	$.sao.Updater.prototype.start	= function () {
		var self = this;
		
		this.getData();
		if(this.intervalID === null)
		{
			this.intervalID = setInterval(function () { self.getData(); }, this.interval * 1000);
		}
	};
	
	/**
	 * Performs an ajax call to the feed for updating
	 *
	 * @this {jQuery.sao.Updater}
	 */
	$.sao.Updater.prototype.getData	= function () {
		var self	= this;
		
		$.ajax(this.feed.uri, {
			dataType: 'json',
			success: function (data, textStatus, jqXHR) { $.sao.ajaxCallback.apply(self, [data, textStatus, jqXHR]); }
		});
	};

	/**
	 * Stops the updater from making any more calls
	 *
	 * @this {jQuery.sao.Updater}
	 */
	$.sao.Updater.prototype.stop	= function () {
		window.clearInterval(this.intervalID);
		this.intervalID = null;
	};
	
	/**
	 * Begins the feed processing by the updator at the given interval
	 *
	 * @param {object} options The options provided when registering a
	 * jQuery.sao.Updater.  The options are merged with the default settings
	 * found at jQuery.sao.settings
	 */
	$.sao.updateGames = function( options ) {
		var feed, updater, settings	= $.extend({}, $.sao.settings, options);
		settings.feedInterval = $.sao.getInvervalValueByLeague(settings.feedParam);
		if(settings.feedParam !== ""){
			$.sao.settings.currentLeague = settings.currentLeague;
			feed	= new $.sao.Feed(settings.feed, settings.feedParam);
			updater	= new $.sao.Updater(feed, settings.selectors, settings.feedInterval, settings.lastModified);

			$.sao.addUpdater(updater);
		}else{
			$.sao.emptyUpdaters();
		}
	};
	/**
	 * Returns interval based ot Leauge param
	 *
	 * @param {string} leagueID The value provided when registering a jQuery.sao.Updater. 
	 * 
	 */
	$.sao.getInvervalValueByLeague = function(leagueID){
		var feedInterval;
		
		switch(leagueID)
		{
		case 'NFL':
		 feedInterval = 20;
		  break;
		case 'NHL':
		 feedInterval = 20;
		  break;
		case 'MLB':
		   feedInterval = 20;
		  break;
		case 'CFL':
		  feedInterval = 45;
		  break;
		case 'NBA':
		   feedInterval = 20;
		  break;
		case 'WNB':
		   feedInterval = 30;
		  break;
		case 'FBC':
		 feedInterval = 20;
		  break;
		case 'BKC':
		 feedInterval = 20;
		  break;
		default:
		   feedInterval = 90;
		}
		return feedInterval;
	};
	/**
	 * Registers an updater.  If an updater with the same feed has already been
	 * registered, then a new one will not be added
	 *
	 * @param {jQuery.sao.Updater} updater The updater to register
	 * 
	 */
	$.sao.addUpdater = function(updater) {
		var i;
		
		for(i = 0; i < $.sao.updaters.length; i++)
		{
			if($.sao.updaters[i].feed.uri === updater.feed.uri)
			{
				return;
			}
		}
		$.sao.updaters[i] = updater;
		updater.start();
	};
	
	/**
	 * Clear inverterval updaters array
	 */
	$.sao.emptyUpdaters = function() {
		
		var i, updatersLength = $.sao.updaters.length;
	
		//if there are instances running kill them (not dead just asleep)
		if(updatersLength > 0){
			for(i=0; i < updatersLength; i++){
				$.sao.updaters[i].stop();
			}
		}	
		
		$.sao.updaters=[];

	};
	
	/**
	 * Clear inverterval updaters array by numer position
	 */
	$.sao.emptyUpdaterByNum = function(numPos) {
		
		if($.sao.updaters[numPos]){
			$.sao.updaters[numPos].stop();
		}
	};
	
	/**
	 * Run through all games pulled back from day feed to check when all games are final.
	 * @param {object} data The feed represented as JSON
	 * 
	 */
	$.sao.checkGames = function(data) {
		var i, dayGame;
		
		//for football feed is set up diffently but will still return a true or false
		if(typeof(data.LEAGUE) === 'undefined'){
			return $.sao.checkGamesWeek(data);
		}
		
		if(data.LEAGUE.length)
		{
			//filter league array to determine which games to check
			for(i = 0; i < data.LEAGUE.length; i++){
				if(data.LEAGUE[i].NAME === $.sao.settings.currentLeague){
					dayGame = data.LEAGUE[i].GAME;
				}
			}
		}
		else
		{
			dayGame = data.LEAGUE.GAME;
		}
		
		if(dayGame)
		{	
			if(dayGame.length)
			{
				for(i = 0; i < dayGame.length; i++)
				{
					if(dayGame[i].GAMESTATUS === "SCHEDULED" || dayGame[i].GAMESTATUS === "INPROGRESS")
					{
						return true;
					}
				}
			}
			else if(dayGame.GAMESTATUS === "SCHEDULED" || dayGame.GAMESTATUS === "INPROGRESS")
			{
				return true;
			}
		}
		
		return false;
	};
	
	/**
	 * Run through all games pulled back from day feed to check when all games are final.
	 * @param {object} data The feed represented as JSON
	 * 
	 */
	$.sao.checkGamesWeek = function(data) {
		var d, l, g, theDay, dayGame;
		
		//run through the days of the week
		for(d = 0; d < data.length; d++){
			theDay = data[d];
			//run through the leagues for the day
			for(l = 0; l < theDay.LEAGUE.length; l++){
				//match current league
				if(theDay.LEAGUE[l].NAME === $.sao.settings.currentLeague){
				
					dayGame = theDay.LEAGUE[l].GAME;
					//more than one game that day
					if(dayGame.length)
					{
						//run through all the games for that current league of day
						for(g = 0; g < dayGame.length; g++)
						{
							//only takes one game to not be final to keep feed going
							if(dayGame[g].GAMESTATUS === "SCHEDULED" || dayGame[g].GAMESTATUS === "INPROGRESS")
							{
								return true;
							}
						}
					}
					else if(dayGame.GAMESTATUS === "SCHEDULED" || dayGame.GAMESTATUS === "INPROGRESS")
					{
						return true;
					}
				}
			}
			
		}
		
		return false;
	};

	/**
	 * Registers an updater.  If an updater with the same feed has already been
	 * registered, then a new one will not be added
	 *
	 * @this {jQuery.sao.Updater}
	 * @param {object} data The feed represented in JSON
	 * @param {string} textStatus The status of the feed response
	 * @param {object} jqXHR The XMLHTTPRequest Object
	 * 
	 */
	$.sao.ajaxCallback	= function (data, textStatus, jqXHR) {
		$.sao.parser.feed(this, data);
	};
	
	$.sao.parser = {};

	/**
	 * Parses the feed after its been collected by the jQuery.sao.Updater.
	 *
	 * @param {jQuery.sao.Updater} updater The updater to register
	 * @param {object} data The feed represented as JSON
	 * 
	 */
	$.sao.parser.feed	= function (updater, data) {
		
		if(data.DAY)
		{
			//if there is still a scheduled game for the week, parse data
			if($.sao.checkGames(data.DAY)){
				$.sao.parser.day(updater, data.DAY);
			}else{
				$.sao.emptyUpdaters();
			}
			
		}else if(data.LEAGUE)
		{
		
			$.sao.parser.league(updater, data.LEAGUE);
		}
	};
	
	/**
	 * Takes a UTC timestamp or string and attempts to convert it into a Date object
	 *
	 * @param {number|string} timestamp The timestamp to convert to an object
	 * @return {Date} If the Date is invalid, then it will return undefined
	 * 
	 */
	$.sao.parser.timestamp	= function (timestamp) {
		if(!isNaN(timestamp))
		{
			return new Date(parseInt(timestamp, 10));
		}
		else if(typeof(timestamp) === "string" && timestamp.length > 0)
		{
			return new Date(timestamp);
		}
		else
		{
			return undefined;
		}
	};
	
	/**
	 * Parses the day objects in the feed
	 *
	 * @param {jQuery.sao.Updater} updater The Updater that requested the feed
	 * @param {object} data The day object or an array of day objects
	 * @param {timestamp} The timestamp from the feed if available
	 * 
	 */
	$.sao.parser.day	= function (updater, data, timestamp) {
		var i, lastModified = timestamp;

		if($.sao.parser.timestamp(data.LASTMODIFIED))
		{
			lastModified = $.sao.parser.timestamp(data.LASTMODIFIED);
		}
		
		// If the data is an array of days then call the function again for each day
		if(data.length)
		{
			for(i = 0; i < data.length; i++)
			{
				$.sao.parser.day(updater, data[i], lastModified);
			}
		}
		else
		{
			if(data.LEAGUE)
			{
				$.sao.parser.league(updater, data.LEAGUE, lastModified);				
			}
		}
	};
	
	/**
	 * Parses the league objects in the feed
	 *
	 * @param {jQuery.sao.Updater} updater The Updater that requested the feed
	 * @param {object} data The league object or an array of day objects
	 * @param {timestamp} The timestamp from the feed if available
	 * 
	 */
	$.sao.parser.league	= function (updater, data, timestamp) {
		
		var i, lastModified = timestamp;

		if($.sao.parser.timestamp(data.LASTMODIFIED))
		{
			lastModified = $.sao.parser.timestamp(data.LASTMODIFIED);
		}

		// If the data is an array of leagues then call the function again for each league
		if(data.length)
		{
			for(i = 0; i < data.length; i++)
			{
				$.sao.parser.league(updater, data[i], lastModified);
			}
		}
		else
		{
			if(data.GAME)
			{
				$.sao.parser.game(updater, data.GAME, lastModified);				
			}
		}
	};
	
	/**
	 * Parses the game objects in the feed.
	 *
	 * @param {jQuery.sao.Updater} updater The Updater that requested the feed
	 * @param {object} data The game object or an array of day objects
	 * @param {timestamp} The timestamp from the feed if available
	 * 
	 */
	$.sao.parser.game = function (updater, data, timestamp) {
	    var i, $awayTeams, $homeTeams, lastModified = timestamp;
	    
	    // The game nodes use the SYSTEMTIME attribute to tell when data for a game was last updated.
	    if($.sao.parser.timestamp(data.SYSTEMTIME))
	    {
	        lastModified = $.sao.parser.timestamp(data.SYSTEMTIME);
	    }
	    else if($.sao.parser.timestamp(data.LASTMODIFIED))
	    {
	        lastModified = $.sao.parser.timestamp(data.LASTMODIFIED);
	    }

	    // If the data is an array of games then call the function again for each games
	    if(data.length)
	    {
	        for(i = 0; i < data.length; i++)
	        {
	            $.sao.parser.game(updater, data[i], lastModified);
	        }
	    }
	    else
	    {
		// If the last modified time of the data on the page (when the
		// updater was registered) is fresher than the feed, then don't update
		// console.log("updater LM: "+updater.lastModified.valueOf()+" LM: "+lastModified.valueOf());
		if(updater.lastModified.valueOf() >= lastModified.valueOf())
		{
			return;
		}

	        // Get Team collections
	        $awayTeams = $("[data-game-id='" + data.PRIMARYID + "'][data-away-team='true']");
	        $homeTeams = $("[data-game-id='" + data.PRIMARYID + "'][data-home-team='true']");
	        $.sao.compare.apply($awayTeams.find(updater.selectors.open), [data.AWAY.INFO.OPENLINE, lastModified]);
	        $.sao.compare.apply($homeTeams.find(updater.selectors.open), [data.HOME.INFO.OPENLINE, lastModified]);
	        $.sao.compare.apply($awayTeams.find(updater.selectors.current), [data.AWAY.INFO.CURRENTLINE, lastModified]);
	        $.sao.compare.apply($homeTeams.find(updater.selectors.current), [data.HOME.INFO.CURRENTLINE, lastModified]);
	        $.sao.compare.apply($awayTeams.find(updater.selectors.halftime), [data.AWAY.INFO.HALFTIMECURRENTLINE, lastModified]);
	        $.sao.compare.apply($homeTeams.find(updater.selectors.halftime), [data.HOME.INFO.HALFTIMECURRENTLINE, lastModified]);

	        if(typeof(data.PROGRESS) !== "undefined" && data.PROGRESS !== "")
	        {
				if(data.GAMESTATUS === "INPROGRESS"){
					$.sao.compare.apply($awayTeams.find(updater.selectors.score), [data.AWAY.INFO.SCORE, data.AWAY.INFO.SCORELASTMODIFIED]);
				}else{
					$.sao.compare.apply($awayTeams.find(updater.selectors.score), [data.AWAY.INFO.PAGESCORE, data.AWAY.INFO.SCORELASTMODIFIED]);
				}
				$.sao.compare.apply($homeTeams.find(updater.selectors.score), [data.HOME.INFO.SCORE, data.HOME.INFO.SCORELASTMODIFIED]);
		        $.sao.compare.apply($awayTeams.find(updater.selectors.progress), [data.PROGRESS, data.PROGRESSLASTMODIFIED]);
		        $.sao.compare.apply($awayTeams.find(updater.selectors.period), [data.PERIOD, data.PERIODLASTMODIFIED]);
	        }
	    }
	};

	$.sao.destroy = function( )  { };

})( jQuery );